# barcode_scanner_controller.py
import serial
import serial.tools.list_ports
import time
import binascii
from flask import jsonify
import paho.mqtt.client as mqtt
import json

class BarcodeScannerController:
    def __init__(self):
        self.mqtt_client = self.init_mqtt_client()
        self.running = False  # 添加运行状态标志
        self.device_commands = [
            {
                "name": "device1",
                "start": bytes.fromhex("16 4A 0d 30 30 31 16 54 0d 2E"),
                "stop": bytes.fromhex("16 4A 0d 30 30 31 16 55 0d 2E"),
                "response": [bytes.fromhex("54 30 30 31 06"), bytes.fromhex("54 30 30 31 15")],
                "close_response": [bytes.fromhex("55 30 30 31 06")]
            },
            {
                "name": "device2",
                "start": bytes.fromhex("16 4A 0d 30 30 32 16 54 0d 2E"),
                "stop": bytes.fromhex("16 4A 0d 30 30 32 16 55 0d 2E"),
                "response": [bytes.fromhex("54 30 30 32 06"), bytes.fromhex("54 30 30 32 15")],
                "close_response": [bytes.fromhex("55 30 30 32 06")]
            },
            # {
            #     "name": "device3",
            #     "start": bytes.fromhex("16 4A 0d 30 30 33 16 54 0d 2E"),
            #     "stop": bytes.fromhex("16 4A 0d 30 30 33 16 55 0d 2E"),
            #     "response": [bytes.fromhex("54 30 30 33 06"), bytes.fromhex("54 30 30 33 15")],
            #     "close_response": [bytes.fromhex("55 30 30 33 06")]
            # }
        ]

    def on_mqtt_connect(self,client, userdata, flags, rc, properties=None):
        if rc == 0:
            print("✅ MQTT连接成功")
        else:
            print(f"❌ MQTT连接失败，错误码: {rc}")

    def on_mqtt_publish(self, client, userdata, mid):
        """消息发布成功回调"""
        print(f"✅ 消息(ID:{mid})发布成功")

    def on_mqtt_disconnect(self, client, userdata, rc, properties=None):
        """MQTT连接断开回调"""
        print(f"⚠️ MQTT连接断开，错误码: {rc}")
        # 自动重连
        try:
            client.reconnect()
            print("🔄 尝试重新连接MQTT...")
        except Exception as e:
            print(f"❌ 自动重连失败: {e}")

    def init_mqtt_client(self):
        """初始化MQTT客户端"""

        client = mqtt.Client()
        client.on_connect = self.on_mqtt_connect
        client.on_publish = self.on_mqtt_publish
        client.on_disconnect = self.on_mqtt_disconnect
        try:
            client.connect("192.168.39.71", 1883, 60)
            client.loop_start()
            print("✅ MQTT客户端初始化完成")
            return client
        except Exception as e:
            print(f"❌ MQTT连接失败: {e}")
            return None

    def get_available_ports(self):
        """Get all available serial ports"""
        ports = serial.tools.list_ports.comports()
        return [port.device for port in ports]

    def check_device_status(self):
        """检测串口设备状态"""
        available_ports = self.get_available_ports()
        expected_ports = 6  # 预期的串口设备数量

        if len(available_ports) < expected_ports:
            offline_devices = expected_ports - len(available_ports)
            status_data = {
                "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
                "status": "部分设备掉线",
                "offline_devices": offline_devices,
                "available_ports": available_ports
            }
        else:
            status_data = {
                "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
                "status": "所有设备正常",
                "available_ports": available_ports
            }

        # 将状态数据转换为 JSON 格式
        status_json = json.dumps(status_data, ensure_ascii=False)

        # 发布设备状态到 MQTT
        self.publish_device_status(status_json)

        # 打印状态信息
        print(f"{time.strftime('%Y-%m-%d %H:%M:%S')} - {status_json}")

    def publish_device_status(self, status):
        heartbeat={"heartbet":1}
        heartbeat_json = json.dumps({"heartbeat": 1}, ensure_ascii=False)
        """发布设备状态到MQTT"""
        if not self.mqtt_client:
            print("❌ MQTT客户端未初始化")
            return False

        topic1 = "/devices/67f4d7b7eb1760189612677f_SKU_SHIBIE/sys/properties/report"
        topic2 = "/devices/67f4d7b7eb1760189612677f_SKU_SHIBIE/sys/messages/up"

        try:
            # QoS=1（至少送达一次），retain=False（不保留消息）
            info = self.mqtt_client.publish(topic1, status, qos=0, retain=False)
            info2 = self.mqtt_client.publish(topic2, heartbeat_json, qos=0, retain=False)

            if info2.rc == mqtt.MQTT_ERR_SUCCESS and info2.rc == mqtt.MQTT_ERR_SUCCESS:
                print(f"📤 已发布消息到 {topic1},{topic2}: {status} (消息ID: {info.mid})")
                return True
            else:
                print(f"❌ 发布失败，错误码: {info2.rc}")
                return False
        except Exception as e:
            print(f"❌ 发布MQTT消息时发生异常: {e}")
            return False

    def open_serial_ports(self, ports, baudrate=9600, timeout=1):
        """Open serial ports"""
        serials = {}
        for port in ports:
            try:
                ser = serial.Serial(port, baudrate=baudrate, timeout=timeout)
                serials[port] = ser
                print(f"串口 {port} 打开成功")
            except serial.SerialException as e:
                print(f"无法打开串口 {port}: {e}")
        return serials

    def close_serial_ports(self, serials):
        """Close all opened serial ports"""
        for port, ser in serials.items():
            if ser.is_open:
                ser.close()
                print(f"串口 {port} 已关闭")

    def send_command_and_listen(self, ports, command, responses, device_name):
        """Send command to devices and listen for response"""
        serials = self.open_serial_ports(ports)

        # Send command to all ports
        for port, ser in serials.items():
            try:
                ser.write(command)
                print(f"已向设备 {device_name} 在串口 {port} 发送命令: {binascii.hexlify(command).decode('ascii')}")
            except serial.SerialException as e:
                print(f"无法向设备 {device_name} 在串口 {port} 发送命令: {e}")

        # Wait for response
        time.sleep(1)

        # Read response from all ports
        for port, ser in serials.items():
            try:
                data = ser.read_all()
                if data:
                    hex_data = binascii.hexlify(data).decode('ascii')
                    print(f"设备 {device_name} 在串口 {port} 返回的十六进制数据: {hex_data}")

                    # Check if response matches expected
                    success = False
                    for response in responses:
                        if response in data:
                            success = True
                            break
                    if success:
                        print(f"设备 {device_name} 在串口 {port} 操作成功")
                    else:
                        print(f"设备 {device_name} 在串口 {port} 操作失败，返回数据: {hex_data}")
                else:
                    print(f"设备 {device_name} 在串口 {port} 无返回数据")
            except serial.SerialException as e:
                print(f"无法读取设备 {device_name} 在串口 {port} 的返回数据: {e}")

        # Close all ports
        self.close_serial_ports(serials)

    def read_serial_data(self):
        """Read barcode data from serial ports"""
        ports = self.get_available_ports()
        serials = self.open_serial_ports(ports)
        barcode_data = None

        try:
            start_time = time.time()
            while time.time() - start_time < 30:  # Wait up to 30 seconds
                for port, ser in serials.items():
                    if ser.is_open and ser.in_waiting > 0:
                        data = ser.read(ser.in_waiting)
                        hex_data = binascii.hexlify(data).decode('ascii')
                        ascii_data = binascii.unhexlify(hex_data).decode('ascii').strip()

                        # Check if valid EAN-13 barcode (13 digits)
                        if len(ascii_data) == 13 and ascii_data.isdigit():
                            barcode_data = ascii_data
                            print(f"从串口 {port} 读取到的有效 EAN-13 码: {barcode_data}")
                            return barcode_data  # Return immediately if valid data found

                        print(f"从串口 {port} 读取到的数据: {ascii_data}")
                time.sleep(0.1)  # Small delay to avoid high CPU usage
        except Exception as e:
            print(f"串口读取错误: {e}")
        finally:
            self.close_serial_ports(serials)

        return barcode_data  # Return None if timeout or no data

    def up_barcode(self):
        """API method to get barcode data"""
        barcode_data = self.read_serial_data()

        if barcode_data is None:
            return jsonify({"code": 500, "data": None, "msg": ["未读取到数据"]})

        return jsonify({"code": 200, "data": {"barcode": barcode_data}, "msg": []})

    def open_scan(self, isOpen):
        """API method to start/stop scanning"""
        ports = self.get_available_ports()

        if isOpen == '1':
            try:
                # Send start command to each device in order
                for device in self.device_commands:
                    self.send_command_and_listen(ports, device["start"], device["response"], device["name"])
                    time.sleep(1)  # 1 second delay between devices
                return jsonify({"code": 200, "data": None, "msg": "Scan started"})
            except Exception as e:
                return jsonify({"code": 500, "data": None, "msg": str(e)}), 500
        elif isOpen == '0':
            try:
                # Send stop command to each device in order
                for device in self.device_commands:
                    self.send_command_and_listen(ports, device["stop"], device["close_response"], device["name"])
                    time.sleep(1)  # 1 second delay between devices
                return jsonify({"code": 200, "data": None, "msg": "Scan stopped"})
            except Exception as e:
                return jsonify({"code": 500, "data": None, "msg": str(e)}), 500
        else:
            return jsonify({"code": 400, "data": None, "msg": "参数错误"}), 400

    def run(self):
        """运行程序，每秒检查一次设备状态"""
        self.running = True
        print("开始定期检查设备状态...")
        try:
            while self.running:
                self.check_device_status()
                time.sleep(1)  # 每秒检查一次
        except KeyboardInterrupt:
            print("停止定期检查设备状态...")
            self.running = False

    def stop(self):
        """停止定期检查"""
        self.running = False